后端生成二维码

This commit is contained in:
orangebabu 2024-08-15 14:35:26 +08:00
parent e6853c1701
commit 474f18f61c
11 changed files with 268 additions and 140 deletions

View File

@ -3,5 +3,6 @@ description = "maxkey-common"
dependencies { dependencies {
//local jars //local jars
implementation fileTree(dir: '../maxkey-lib/', include: '*/*.jar') implementation fileTree(dir: '../maxkey-lib/', include: '*/*.jar')
}
}

View File

@ -1,19 +1,19 @@
/* /*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top] * Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.dromara.maxkey.util; package org.dromara.maxkey.util;
@ -27,58 +27,58 @@ import com.google.zxing.common.BitMatrix;
public class RQCodeUtils { public class RQCodeUtils {
public static void write2File(String path,String rqCodeText,String format,int width, int height ){ public static void write2File(String path,String rqCodeText,String format,int width, int height ){
try { try {
BitMatrix byteMatrix=genRQCode(rqCodeText,width,height); BitMatrix byteMatrix=genRQCode(rqCodeText,width,height);
File file = new File(path); File file = new File(path);
QRCode.writeToPath(byteMatrix, format, file); QRCode.writeToPath(byteMatrix, format, file);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
public static BufferedImage write2BufferedImage(String rqCodeText,String format,int width, int height ){ public static BufferedImage write2BufferedImage(String rqCodeText,String format,int width, int height){
try { try {
BitMatrix byteMatrix=genRQCode(rqCodeText,width,height); BitMatrix byteMatrix=genRQCode(rqCodeText,width,height);
return QRCode.toBufferedImage(byteMatrix); return QRCode.toBufferedImage(byteMatrix);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
return null; return null;
} }
public static void write2OutputStream(OutputStream stream,String rqCodeText,String format,int width, int height ){ public static void write2OutputStream(OutputStream stream,String rqCodeText,String format,int width, int height ){
try { try {
BitMatrix byteMatrix=genRQCode(rqCodeText,width,height); BitMatrix byteMatrix=genRQCode(rqCodeText,width,height);
QRCode.writeToStream(byteMatrix, format, stream); QRCode.writeToStream(byteMatrix, format, stream);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
public static BitMatrix genRQCode(String rqCodeText,int width, int height ){ public static BitMatrix genRQCode(String rqCodeText,int width, int height){
if(width==0){ if(width==0){
width=200; width=200;
} }
if(height==0){ if(height==0){
height=200; height=200;
} }
try { try {
return new MultiFormatWriter().encode( return new MultiFormatWriter().encode(
rqCodeText, rqCodeText,
BarcodeFormat.QR_CODE, BarcodeFormat.QR_CODE,
width, width,
height); height);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
return null; return null;
} }
} }

View File

@ -1,19 +1,19 @@
/* /*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top] * Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.dromara.maxkey.persistence.redis; package org.dromara.maxkey.persistence.redis;
@ -26,7 +26,7 @@ import redis.clients.jedis.JedisPoolConfig;
public class RedisConnectionFactory { public class RedisConnectionFactory {
private static final Logger _logger = LoggerFactory.getLogger(RedisConnectionFactory.class); private static final Logger _logger = LoggerFactory.getLogger(RedisConnectionFactory.class);
public static class DEFAULT_CONFIG { public static class DEFAULT_CONFIG {
/** /**
* Redis默认服务器IP * Redis默认服务器IP
@ -95,7 +95,7 @@ public class RedisConnectionFactory {
timeOut = DEFAULT_CONFIG.DEFAULT_TIMEOUT; timeOut = DEFAULT_CONFIG.DEFAULT_TIMEOUT;
} }
if (this.password == null || this.password.equals("") || this.password.equalsIgnoreCase("password")) { if (this.password == null || this.password.equals("")) {
this.password = null; this.password = null;
} }
jedisPool = new JedisPool(poolConfig, hostName, port, timeOut, password); jedisPool = new JedisPool(poolConfig, hostName, port, timeOut, password);
@ -120,7 +120,7 @@ public class RedisConnectionFactory {
Jedis jedis = jedisPool.getResource(); Jedis jedis = jedisPool.getResource();
_logger.trace("return jedisPool Resource ."); _logger.trace("return jedisPool Resource .");
return jedis; return jedis;
} }
public void close(Jedis conn) { public void close(Jedis conn) {
@ -130,7 +130,7 @@ public class RedisConnectionFactory {
_logger.trace("closed conn ."); _logger.trace("closed conn .");
} }
public String getHostName() { public String getHostName() {
return hostName; return hostName;
} }
@ -170,5 +170,5 @@ public class RedisConnectionFactory {
public JedisPoolConfig getPoolConfig() { public JedisPoolConfig getPoolConfig() {
return poolConfig; return poolConfig;
} }
} }

View File

@ -1,19 +1,19 @@
/* /*
* Copyright [2022] [MaxKey of copyright http://www.maxkey.top] * Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.dromara.maxkey.persistence.repository; package org.dromara.maxkey.persistence.repository;
@ -23,38 +23,38 @@ import java.util.List;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.ObjectUtils;
import org.dromara.maxkey.entity.Institutions; import org.dromara.maxkey.entity.Institutions;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.RowMapper;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.Caffeine;
public class InstitutionsRepository { public class InstitutionsRepository {
static final Logger _logger = LoggerFactory.getLogger(InstitutionsRepository.class); static final Logger _logger = LoggerFactory.getLogger(InstitutionsRepository.class);
private static final String SELECT_STATEMENT = private static final String SELECT_STATEMENT =
"select * from mxk_institutions where id = ? or domain = ? or consoledomain = ?" ; "select * from mxk_institutions where id = ? or domain = ? or consoledomain = ?" ;
private static final String DEFAULT_INSTID = "1"; private static final String DEFAULT_INSTID = "1";
protected static final Cache<String, Institutions> institutionsStore = protected static final Cache<String, Institutions> institutionsStore =
Caffeine.newBuilder() Caffeine.newBuilder()
.expireAfterWrite(60, TimeUnit.MINUTES) .expireAfterWrite(60, TimeUnit.MINUTES)
.build(); .build();
//id domain mapping //id domain mapping
protected static final ConcurrentHashMap<String,String> mapper = new ConcurrentHashMap<>(); protected static final ConcurrentHashMap<String,String> mapper = new ConcurrentHashMap<>();
protected JdbcTemplate jdbcTemplate; protected JdbcTemplate jdbcTemplate;
public InstitutionsRepository(JdbcTemplate jdbcTemplate) { public InstitutionsRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate; this.jdbcTemplate = jdbcTemplate;
} }
public Institutions get(String instIdOrDomain) { public Institutions get(String instIdOrDomain) {
_logger.trace(" instId {}" , instIdOrDomain); _logger.trace(" instId {}" , instIdOrDomain);
Institutions inst = getByInstIdOrDomain(instIdOrDomain); Institutions inst = getByInstIdOrDomain(instIdOrDomain);
@ -64,15 +64,15 @@ public class InstitutionsRepository {
} }
return inst; return inst;
} }
private Institutions getByInstIdOrDomain(String instIdOrDomain) { private Institutions getByInstIdOrDomain(String instIdOrDomain) {
_logger.trace(" instId {}" , instIdOrDomain); _logger.trace(" instId {}" , instIdOrDomain);
Institutions inst = institutionsStore.getIfPresent(mapper.get(instIdOrDomain)==null ? DEFAULT_INSTID : mapper.get(instIdOrDomain) ); Institutions inst = institutionsStore.getIfPresent(mapper.get(instIdOrDomain)==null ? DEFAULT_INSTID : mapper.get(instIdOrDomain) );
if(inst == null) { if(inst == null) {
List<Institutions> institutions = List<Institutions> institutions =
jdbcTemplate.query(SELECT_STATEMENT,new InstitutionsRowMapper(),instIdOrDomain,instIdOrDomain,instIdOrDomain); jdbcTemplate.query(SELECT_STATEMENT,new InstitutionsRowMapper(),instIdOrDomain,instIdOrDomain,instIdOrDomain);
if (CollectionUtils.isNotEmpty(institutions)) { if (ObjectUtils.isNotEmpty(institutions)) {
inst = institutions.get(0); inst = institutions.get(0);
} }
if(inst != null ) { if(inst != null ) {
@ -81,10 +81,10 @@ public class InstitutionsRepository {
mapper.put(inst.getId(), inst.getDomain()); mapper.put(inst.getId(), inst.getDomain());
} }
} }
return inst; return inst;
} }
public class InstitutionsRowMapper implements RowMapper<Institutions> { public class InstitutionsRowMapper implements RowMapper<Institutions> {
@Override @Override
public Institutions mapRow(ResultSet rs, int rowNum) throws SQLException { public Institutions mapRow(ResultSet rs, int rowNum) throws SQLException {

View File

@ -1,19 +1,19 @@
/* /*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top] * Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.dromara.maxkey.persistence.repository; package org.dromara.maxkey.persistence.repository;
@ -24,13 +24,12 @@ import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.dromara.maxkey.constants.ConstsRoles; import org.dromara.maxkey.constants.ConstsRoles;
import org.dromara.maxkey.constants.ConstsStatus; import org.dromara.maxkey.constants.ConstsStatus;
import org.dromara.maxkey.entity.idm.Groups; import org.dromara.maxkey.entity.idm.Groups;
import org.dromara.maxkey.entity.idm.UserInfo; import org.dromara.maxkey.entity.idm.UserInfo;
import org.dromara.maxkey.util.StrUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
@ -57,28 +56,28 @@ public class LoginRepository {
private static final String GROUPS_SELECT_STATEMENT = "select distinct g.id,g.groupcode,g.groupname from mxk_userinfo u,mxk_groups g,mxk_group_member gm where u.id = ? and u.id=gm.memberid and gm.groupid=g.id "; private static final String GROUPS_SELECT_STATEMENT = "select distinct g.id,g.groupcode,g.groupname from mxk_userinfo u,mxk_groups g,mxk_group_member gm where u.id = ? and u.id=gm.memberid and gm.groupid=g.id ";
private static final String DEFAULT_USERINFO_SELECT_STATEMENT = "select * from mxk_userinfo where username = ? "; private static final String DEFAULT_USERINFO_SELECT_STATEMENT = "select * from mxk_userinfo where username = ? ";
private static final String DEFAULT_USERINFO_SELECT_STATEMENT_USERNAME_MOBILE = "select * from mxk_userinfo where (username = ? or mobile = ?)"; private static final String DEFAULT_USERINFO_SELECT_STATEMENT_USERNAME_MOBILE = "select * from mxk_userinfo where (username = ? or mobile = ?)";
private static final String DEFAULT_USERINFO_SELECT_STATEMENT_USERNAME_MOBILE_EMAIL = "select * from mxk_userinfo where (username = ? or mobile = ? or email = ?) "; private static final String DEFAULT_USERINFO_SELECT_STATEMENT_USERNAME_MOBILE_EMAIL = "select * from mxk_userinfo where (username = ? or mobile = ? or email = ?) ";
private static final String DEFAULT_MYAPPS_SELECT_STATEMENT = "select distinct app.id,app.appname from mxk_apps app,mxk_access gp,mxk_groups g where app.id=gp.appid and app.status = 1 and gp.groupid=g.id and g.id in(%s)"; private static final String DEFAULT_MYAPPS_SELECT_STATEMENT = "select distinct app.id,app.appname from mxk_apps app,mxk_access gp,mxk_groups g where app.id=gp.appid and app.status = 1 and gp.groupid=g.id and g.id in(%s)";
protected JdbcTemplate jdbcTemplate; protected JdbcTemplate jdbcTemplate;
/** /**
* 1 (USERNAME) 2 (USERNAME | MOBILE) 3 (USERNAME | MOBILE | EMAIL) * 1 (USERNAME) 2 (USERNAME | MOBILE) 3 (USERNAME | MOBILE | EMAIL)
*/ */
public static int LOGIN_ATTRIBUTE_TYPE = 2; public static int LOGIN_ATTRIBUTE_TYPE = 2;
public LoginRepository(){ public LoginRepository(){
} }
public LoginRepository(JdbcTemplate jdbcTemplate){ public LoginRepository(JdbcTemplate jdbcTemplate){
this.jdbcTemplate=jdbcTemplate; this.jdbcTemplate=jdbcTemplate;
} }
public UserInfo find(String username, String password) { public UserInfo find(String username, String password) {
List<UserInfo> listUserInfo = null ; List<UserInfo> listUserInfo = null ;
if( LOGIN_ATTRIBUTE_TYPE == 1) { if( LOGIN_ATTRIBUTE_TYPE == 1) {
@ -89,37 +88,37 @@ public class LoginRepository {
listUserInfo = findByUsernameOrMobileOrEmail(username,password); listUserInfo = findByUsernameOrMobileOrEmail(username,password);
} }
_logger.debug("load UserInfo : {}" , listUserInfo); _logger.debug("load UserInfo : {}" , listUserInfo);
return (CollectionUtils.isNotEmpty(listUserInfo))? listUserInfo.get(0) : null; return (ObjectUtils.isNotEmpty(listUserInfo))? listUserInfo.get(0) : null;
} }
public List<UserInfo> findByUsername(String username, String password) { public List<UserInfo> findByUsername(String username, String password) {
return jdbcTemplate.query( return jdbcTemplate.query(
DEFAULT_USERINFO_SELECT_STATEMENT, DEFAULT_USERINFO_SELECT_STATEMENT,
new UserInfoRowMapper(), new UserInfoRowMapper(),
username username
); );
} }
public List<UserInfo> findByUsernameOrMobile(String username, String password) { public List<UserInfo> findByUsernameOrMobile(String username, String password) {
return jdbcTemplate.query( return jdbcTemplate.query(
DEFAULT_USERINFO_SELECT_STATEMENT_USERNAME_MOBILE, DEFAULT_USERINFO_SELECT_STATEMENT_USERNAME_MOBILE,
new UserInfoRowMapper(), new UserInfoRowMapper(),
username,username username,username
); );
} }
public List<UserInfo> findByUsernameOrMobileOrEmail(String username, String password) { public List<UserInfo> findByUsernameOrMobileOrEmail(String username, String password) {
return jdbcTemplate.query( return jdbcTemplate.query(
DEFAULT_USERINFO_SELECT_STATEMENT_USERNAME_MOBILE_EMAIL, DEFAULT_USERINFO_SELECT_STATEMENT_USERNAME_MOBILE_EMAIL,
new UserInfoRowMapper(), new UserInfoRowMapper(),
username,username,username username,username,username
); );
} }
/** /**
* 閿佸畾鐢ㄦ埛锛歩slock锛<EFBFBD>1 鐢ㄦ埛瑙i攣 2 鐢ㄦ埛閿佸畾 * 閿佸畾鐢ㄦ埛锛歩slock锛<EFBFBD>1 鐢ㄦ埛瑙i攣 2 鐢ㄦ埛閿佸畾
* *
* @param userInfo * @param userInfo
*/ */
public void updateLock(UserInfo userInfo) { public void updateLock(UserInfo userInfo) {
@ -137,7 +136,7 @@ public class LoginRepository {
/** /**
* 閿佸畾鐢ㄦ埛锛歩slock锛<EFBFBD>1 鐢ㄦ埛瑙i攣 2 鐢ㄦ埛閿佸畾 * 閿佸畾鐢ㄦ埛锛歩slock锛<EFBFBD>1 鐢ㄦ埛瑙i攣 2 鐢ㄦ埛閿佸畾
* *
* @param userInfo * @param userInfo
*/ */
public void updateUnlock(UserInfo userInfo) { public void updateUnlock(UserInfo userInfo) {
@ -155,7 +154,7 @@ public class LoginRepository {
/** /**
* reset BadPasswordCount And Lockout * reset BadPasswordCount And Lockout
* *
* @param userInfo * @param userInfo
*/ */
public void updateLockout(UserInfo userInfo) { public void updateLockout(UserInfo userInfo) {
@ -173,7 +172,7 @@ public class LoginRepository {
/** /**
* if login password is error ,BadPasswordCount++ and set bad date * if login password is error ,BadPasswordCount++ and set bad date
* *
* @param userInfo * @param userInfo
*/ */
public void updateBadPasswordCount(UserInfo userInfo) { public void updateBadPasswordCount(UserInfo userInfo) {
@ -190,15 +189,15 @@ public class LoginRepository {
_logger.error(e.getMessage()); _logger.error(e.getMessage());
} }
} }
public List<GrantedAuthority> queryAuthorizedApps(List<GrantedAuthority> grantedAuthoritys) { public List<GrantedAuthority> queryAuthorizedApps(List<GrantedAuthority> grantedAuthoritys) {
String grantedAuthorityString="'ROLE_ALL_USER'"; String grantedAuthorityString="'ROLE_ALL_USER'";
for(GrantedAuthority grantedAuthority : grantedAuthoritys) { for(GrantedAuthority grantedAuthority : grantedAuthoritys) {
grantedAuthorityString += ",'"+ grantedAuthority.getAuthority()+"'"; grantedAuthorityString += ",'"+ grantedAuthority.getAuthority()+"'";
} }
ArrayList<GrantedAuthority> listAuthorizedApps = (ArrayList<GrantedAuthority>) jdbcTemplate.query( ArrayList<GrantedAuthority> listAuthorizedApps = (ArrayList<GrantedAuthority>) jdbcTemplate.query(
String.format(DEFAULT_MYAPPS_SELECT_STATEMENT, grantedAuthorityString), String.format(DEFAULT_MYAPPS_SELECT_STATEMENT, grantedAuthorityString),
new RowMapper<GrantedAuthority>() { new RowMapper<GrantedAuthority>() {
public GrantedAuthority mapRow(ResultSet rs, int rowNum) throws SQLException { public GrantedAuthority mapRow(ResultSet rs, int rowNum) throws SQLException {
return new SimpleGrantedAuthority(rs.getString("id")); return new SimpleGrantedAuthority(rs.getString("id"));
@ -208,7 +207,7 @@ public class LoginRepository {
_logger.debug("list Authorized Apps {}" , listAuthorizedApps); _logger.debug("list Authorized Apps {}" , listAuthorizedApps);
return listAuthorizedApps; return listAuthorizedApps;
} }
public List<Groups> queryGroups(UserInfo userInfo) { public List<Groups> queryGroups(UserInfo userInfo) {
List<Groups> listRoles = jdbcTemplate.query(GROUPS_SELECT_STATEMENT, new RowMapper<Groups>() { List<Groups> listRoles = jdbcTemplate.query(GROUPS_SELECT_STATEMENT, new RowMapper<Groups>() {
public Groups mapRow(ResultSet rs, int rowNum) throws SQLException { public Groups mapRow(ResultSet rs, int rowNum) throws SQLException {
@ -222,7 +221,7 @@ public class LoginRepository {
/** /**
* grant Authority by userinfo * grant Authority by userinfo
* *
* @param userInfo * @param userInfo
* @return ArrayList<GrantedAuthority> * @return ArrayList<GrantedAuthority>
*/ */
@ -237,7 +236,7 @@ public class LoginRepository {
grantedAuthority.add(ConstsRoles.ROLE_ORDINARY_USER); grantedAuthority.add(ConstsRoles.ROLE_ORDINARY_USER);
for (Groups group : listGroups) { for (Groups group : listGroups) {
grantedAuthority.add(new SimpleGrantedAuthority(group.getId())); grantedAuthority.add(new SimpleGrantedAuthority(group.getId()));
if(group.getGroupCode().startsWith("ROLE_") if(group.getGroupCode().startsWith("ROLE_")
&& !grantedAuthority.contains(new SimpleGrantedAuthority(group.getGroupCode()))) { && !grantedAuthority.contains(new SimpleGrantedAuthority(group.getGroupCode()))) {
grantedAuthority.add(new SimpleGrantedAuthority(group.getGroupCode())); grantedAuthority.add(new SimpleGrantedAuthority(group.getGroupCode()));
} }
@ -246,19 +245,19 @@ public class LoginRepository {
return grantedAuthority; return grantedAuthority;
} }
public void updateLastLogin(UserInfo userInfo) { public void updateLastLogin(UserInfo userInfo) {
jdbcTemplate.update(LOGIN_USERINFO_UPDATE_STATEMENT, jdbcTemplate.update(LOGIN_USERINFO_UPDATE_STATEMENT,
new Object[] { new Object[] {
userInfo.getLastLoginTime(), userInfo.getLastLoginTime(),
userInfo.getLastLoginIp(), userInfo.getLastLoginIp(),
userInfo.getLoginCount() + 1, userInfo.getLoginCount() + 1,
userInfo.getId() userInfo.getId()
}, },
new int[] { Types.TIMESTAMP, Types.VARCHAR, Types.INTEGER, Types.VARCHAR }); new int[] { Types.TIMESTAMP, Types.VARCHAR, Types.INTEGER, Types.VARCHAR });
} }
public class UserInfoRowMapper implements RowMapper<UserInfo> { public class UserInfoRowMapper implements RowMapper<UserInfo> {
@Override @Override
public UserInfo mapRow(ResultSet rs, int rowNum) throws SQLException { public UserInfo mapRow(ResultSet rs, int rowNum) throws SQLException {
@ -372,7 +371,7 @@ public class LoginRepository {
if (userInfo.getTheme() == null || userInfo.getTheme().equalsIgnoreCase("")) { if (userInfo.getTheme() == null || userInfo.getTheme().equalsIgnoreCase("")) {
userInfo.setTheme("default"); userInfo.setTheme("default");
} }
return userInfo; return userInfo;
} }
} }

View File

@ -0,0 +1,63 @@
package org.dromara.maxkey.persistence.service;
import org.dromara.maxkey.persistence.cache.MomentaryService;
import org.dromara.maxkey.util.IdGenerator;
import org.dromara.maxkey.util.ObjectTransformer;
import org.dromara.mybatis.jpa.id.IdentifierGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.time.Duration;
/**
* @description:
* @author: orangeBabu
* @time: 15/8/2024 AM9:49
*/
@Repository
public class ScanCodeService {
private static final Logger _logger = LoggerFactory.getLogger(ScanCodeService.class);
static final String SCANCODE_TICKET = "login:scancode:%s";
static final String SCANCODE_CONFIRM = "login:scancode:confirm:%s";
public static class STATE{
public static final String SCANED = "scaned";
public static final String CONFIRMED = "confirmed";
public static final String CANCELED = "canceled";
public static final String CANCEL = "cancel";
public static final String CONFIRM = "confirm";
}
int validitySeconds = 60 * 3; //default 3 minutes.
int cancelValiditySeconds = 60 * 1; //default 1 minutes.
@Autowired
IdGenerator idGenerator;
@Autowired
MomentaryService momentaryService;
private String getKey(Long ticket) {
return SCANCODE_TICKET.formatted(ticket);
}
private String getConfirmKey(Long sessionId) {
return SCANCODE_CONFIRM.formatted(sessionId);
}
public Long createTicket() {
Long ticket = 0L;
ticket = Long.parseLong(idGenerator.generate());
momentaryService.put(getKey(ticket), "ORCode", Duration.ofSeconds(validitySeconds));
_logger.info("Ticket {} , Duration {}", ticket , Duration.ofSeconds(validitySeconds));
return ticket;
}
}

View File

@ -127,6 +127,7 @@
}, },
"defaultProject": "ng-alain", "defaultProject": "ng-alain",
"cli": { "cli": {
"analytics": false,
"packageManager": "yarn" "packageManager": "yarn"
} }
} }

View File

@ -14,7 +14,7 @@
<i nz-icon nzType="mobile" nzTheme="outline"></i> <i nz-icon nzType="mobile" nzTheme="outline"></i>
{{ 'mxk.login.tab-mobile' | i18n }} {{ 'mxk.login.tab-mobile' | i18n }}
</label> </label>
<label nz-radio-button nzValue="qrscan" style="width: 50%; text-align: center" (click)="getQrCode()"> <label nz-radio-button nzValue="qrscan" style="width: 50%; text-align: center" (click)="getLoginQrCode()">
<i nz-icon nzType="qrcode" nzTheme="outline"></i>{{ 'mxk.login.tab-qrscan' | i18n }} <i nz-icon nzType="qrcode" nzTheme="outline"></i>{{ 'mxk.login.tab-qrscan' | i18n }}
</label> </label>
</nz-radio-group> </nz-radio-group>
@ -85,7 +85,7 @@
<div nz-row *ngIf="loginType=='qrscan'"> <div nz-row *ngIf="loginType=='qrscan'">
<div class="qrcode" id="div_qrcodelogin" style="background: #fff;padding: 20px;"> </div> <div class="qrcode" id="div_qrcodelogin" style="background: #fff;padding: 20px;"> </div>
<div id="qrexpire" *ngIf="qrexpire" style="width: 100%;text-align: center;background: #fff;padding-bottom: 20px;"> <div id="qrexpire" *ngIf="qrexpire" style="width: 100%;text-align: center;background: #fff;padding-bottom: 20px;">
二维码过期 <a href="javascript:" (click)="getQrCode()">刷新</a> 二维码过期 <a href="javascript:" (click)="getLoginQrCode()">刷新</a>
</div> </div>
</div> </div>
<nz-form-item *ngIf="loginType == 'normal' || loginType == 'mobile'"> <nz-form-item *ngIf="loginType == 'normal' || loginType == 'mobile'">

View File

@ -29,6 +29,7 @@ import { finalize } from 'rxjs/operators';
import { AuthnService } from '../../../service/authn.service'; import { AuthnService } from '../../../service/authn.service';
import { ImageCaptchaService } from '../../../service/image-captcha.service'; import { ImageCaptchaService } from '../../../service/image-captcha.service';
import { SocialsProviderService } from '../../../service/socials-provider.service'; import { SocialsProviderService } from '../../../service/socials-provider.service';
import {QrCodeService} from "../../../service/QrCode.service";
import { CONSTS } from '../../../shared/consts'; import { CONSTS } from '../../../shared/consts';
import { stringify } from 'querystring'; import { stringify } from 'querystring';
@ -68,6 +69,7 @@ export class UserLoginComponent implements OnInit, OnDestroy {
private authnService: AuthnService, private authnService: AuthnService,
private socialsProviderService: SocialsProviderService, private socialsProviderService: SocialsProviderService,
private imageCaptchaService: ImageCaptchaService, private imageCaptchaService: ImageCaptchaService,
private qrCodeService: QrCodeService,
@Optional() @Optional()
@Inject(ReuseTabService) @Inject(ReuseTabService)
private reuseTabService: ReuseTabService, private reuseTabService: ReuseTabService,
@ -296,6 +298,14 @@ export class UserLoginComponent implements OnInit, OnDestroy {
}); });
} }
getLoginQrCode() {
this.qrCodeService.getLoginQrCode().subscribe(res => {
if (res.code === 0) {
}
})
}
getQrCode(): void { getQrCode(): void {
this.qrexpire = false; this.qrexpire = false;
if (this.interval$) { if (this.interval$) {

View File

@ -0,0 +1,29 @@
/*
* Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { SettingsService, _HttpClient, User } from '@delon/theme';
@Injectable({
providedIn: 'root'
})
export class QrCodeService {
constructor(private http: _HttpClient) {}
getLoginQrCode() {
return this.http.get('/login/genScanCode');
}
}

View File

@ -1,22 +1,23 @@
/* /*
* Copyright [2022] [MaxKey of copyright http://www.maxkey.top] * Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.dromara.maxkey.web.contorller; package org.dromara.maxkey.web.contorller;
import java.awt.image.BufferedImage;
import java.text.ParseException; import java.text.ParseException;
import java.util.HashMap; import java.util.HashMap;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -28,14 +29,19 @@ import org.dromara.maxkey.authn.support.kerberos.KerberosService;
import org.dromara.maxkey.authn.support.rememberme.AbstractRemeberMeManager; import org.dromara.maxkey.authn.support.rememberme.AbstractRemeberMeManager;
import org.dromara.maxkey.authn.support.rememberme.RemeberMe; import org.dromara.maxkey.authn.support.rememberme.RemeberMe;
import org.dromara.maxkey.authn.support.socialsignon.service.SocialSignOnProviderService; import org.dromara.maxkey.authn.support.socialsignon.service.SocialSignOnProviderService;
import org.dromara.maxkey.authn.web.AuthorizationUtils;
import org.dromara.maxkey.configuration.ApplicationConfig; import org.dromara.maxkey.configuration.ApplicationConfig;
import org.dromara.maxkey.constants.ConstsLoginType; import org.dromara.maxkey.constants.ConstsLoginType;
import org.dromara.maxkey.crypto.Base64Utils;
import org.dromara.maxkey.crypto.password.PasswordReciprocal;
import org.dromara.maxkey.entity.*; import org.dromara.maxkey.entity.*;
import org.dromara.maxkey.entity.idm.UserInfo; import org.dromara.maxkey.entity.idm.UserInfo;
import org.dromara.maxkey.password.onetimepwd.AbstractOtpAuthn; import org.dromara.maxkey.password.onetimepwd.AbstractOtpAuthn;
import org.dromara.maxkey.password.sms.SmsOtpAuthnService; import org.dromara.maxkey.password.sms.SmsOtpAuthnService;
import org.dromara.maxkey.persistence.service.ScanCodeService;
import org.dromara.maxkey.persistence.service.SocialsAssociatesService; import org.dromara.maxkey.persistence.service.SocialsAssociatesService;
import org.dromara.maxkey.persistence.service.UserInfoService; import org.dromara.maxkey.persistence.service.UserInfoService;
import org.dromara.maxkey.util.RQCodeUtils;
import org.dromara.maxkey.web.WebConstants; import org.dromara.maxkey.web.WebConstants;
import org.dromara.maxkey.web.WebContext; import org.dromara.maxkey.web.WebContext;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -56,6 +62,8 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import static org.reflections.Reflections.log;
/** /**
* @author Crystal.Sea * @author Crystal.Sea
* *
@ -65,13 +73,13 @@ import jakarta.servlet.http.HttpServletResponse;
@RequestMapping(value = "/login") @RequestMapping(value = "/login")
public class LoginEntryPoint { public class LoginEntryPoint {
private static Logger logger = LoggerFactory.getLogger(LoginEntryPoint.class); private static Logger logger = LoggerFactory.getLogger(LoginEntryPoint.class);
@Autowired @Autowired
AuthTokenService authTokenService; AuthTokenService authTokenService;
@Autowired @Autowired
ApplicationConfig applicationConfig; ApplicationConfig applicationConfig;
@Autowired @Autowired
AbstractAuthenticationProvider authenticationProvider ; AbstractAuthenticationProvider authenticationProvider ;
@ -80,24 +88,25 @@ public class LoginEntryPoint {
@Autowired @Autowired
SocialsAssociatesService socialsAssociatesService; SocialsAssociatesService socialsAssociatesService;
@Autowired @Autowired
KerberosService kerberosService; KerberosService kerberosService;
@Autowired @Autowired
UserInfoService userInfoService; UserInfoService userInfoService;
@Autowired @Autowired
AbstractOtpAuthn tfaOtpAuthn; AbstractOtpAuthn tfaOtpAuthn;
@Autowired @Autowired
SmsOtpAuthnService smsAuthnService; SmsOtpAuthnService smsAuthnService;
@Autowired
ScanCodeService scanCodeService;
@Autowired @Autowired
AbstractRemeberMeManager remeberMeManager; AbstractRemeberMeManager remeberMeManager;
/** /**
* init login * init login
* @return * @return
@ -133,11 +142,11 @@ public class LoginEntryPoint {
model.put("otpType", tfaOtpAuthn.getOtpType()); model.put("otpType", tfaOtpAuthn.getOtpType());
model.put("otpInterval", tfaOtpAuthn.getInterval()); model.put("otpInterval", tfaOtpAuthn.getInterval());
} }
if( applicationConfig.getLoginConfig().isKerberos()){ if( applicationConfig.getLoginConfig().isKerberos()){
model.put("userDomainUrlJson", kerberosService.buildKerberosProxys()); model.put("userDomainUrlJson", kerberosService.buildKerberosProxys());
} }
Institutions inst = (Institutions)WebContext.getAttribute(WebConstants.CURRENT_INST); Institutions inst = (Institutions)WebContext.getAttribute(WebConstants.CURRENT_INST);
model.put("inst", inst); model.put("inst", inst);
if(applicationConfig.getLoginConfig().isCaptcha()) { if(applicationConfig.getLoginConfig().isCaptcha()) {
@ -146,10 +155,10 @@ public class LoginEntryPoint {
model.put("state", authTokenService.genRandomJwt()); model.put("state", authTokenService.genRandomJwt());
//load Social Sign On Providers //load Social Sign On Providers
model.put("socials", socialSignOnProviderService.loadSocials(inst.getId())); model.put("socials", socialSignOnProviderService.loadSocials(inst.getId()));
return new Message<HashMap<String , Object>>(model); return new Message<HashMap<String , Object>>(model);
} }
@RequestMapping(value={"/sendotp/{mobile}"}, produces = {MediaType.APPLICATION_JSON_VALUE}) @RequestMapping(value={"/sendotp/{mobile}"}, produces = {MediaType.APPLICATION_JSON_VALUE})
public Message<AuthJwt> produceOtp(@PathVariable("mobile") String mobile) { public Message<AuthJwt> produceOtp(@PathVariable("mobile") String mobile) {
@ -158,7 +167,7 @@ public class LoginEntryPoint {
smsAuthnService.getByInstId(WebContext.getInst().getId()).produce(userInfo); smsAuthnService.getByInstId(WebContext.getInst().getId()).produce(userInfo);
return new Message<AuthJwt>(Message.SUCCESS); return new Message<AuthJwt>(Message.SUCCESS);
} }
return new Message<AuthJwt>(Message.FAIL); return new Message<AuthJwt>(Message.FAIL);
} }
@ -202,7 +211,7 @@ public class LoginEntryPoint {
return new Message<AuthJwt>(Message.FAIL); return new Message<AuthJwt>(Message.FAIL);
} }
/** /**
* normal * normal
* @param loginCredential * @param loginCredential
@ -216,7 +225,7 @@ public class LoginEntryPoint {
String authType = credential.getAuthType(); String authType = credential.getAuthType();
logger.debug("Login AuthN Type {}" , authType); logger.debug("Login AuthN Type {}" , authType);
if (StringUtils.isNotBlank(authType)){ if (StringUtils.isNotBlank(authType)){
Authentication authentication = authenticationProvider.authenticate(credential); Authentication authentication = authenticationProvider.authenticate(credential);
if(authentication != null) { if(authentication != null) {
AuthJwt authJwt = authTokenService.genAuthJwt(authentication); AuthJwt authJwt = authTokenService.genAuthJwt(authentication);
if(StringUtils.isNotBlank(credential.getRemeberMe()) if(StringUtils.isNotBlank(credential.getRemeberMe())
@ -229,9 +238,9 @@ public class LoginEntryPoint {
(Integer)WebContext.getAttribute(WebConstants.CURRENT_USER_PASSWORD_SET_TYPE)); (Integer)WebContext.getAttribute(WebConstants.CURRENT_USER_PASSWORD_SET_TYPE));
} }
authJwtMessage = new Message<>(authJwt); authJwtMessage = new Message<>(authJwt);
}else {//fail }else {//fail
String errorMsg = WebContext.getAttribute(WebConstants.LOGIN_ERROR_SESSION_MESSAGE) == null ? String errorMsg = WebContext.getAttribute(WebConstants.LOGIN_ERROR_SESSION_MESSAGE) == null ?
"" : WebContext.getAttribute(WebConstants.LOGIN_ERROR_SESSION_MESSAGE).toString(); "" : WebContext.getAttribute(WebConstants.LOGIN_ERROR_SESSION_MESSAGE).toString();
authJwtMessage.setMessage(errorMsg); authJwtMessage.setMessage(errorMsg);
logger.debug("login fail , message {}",errorMsg); logger.debug("login fail , message {}",errorMsg);
@ -242,7 +251,7 @@ public class LoginEntryPoint {
} }
return authJwtMessage; return authJwtMessage;
} }
/** /**
* for congress * for congress
* @param loginCredential * @param loginCredential
@ -259,4 +268,20 @@ public class LoginEntryPoint {
return new Message<>(Message.FAIL); return new Message<>(Message.FAIL);
} }
@Operation(summary = "生成登录扫描二维码", description = "生成登录扫描二维码", method = "GET")
@GetMapping("/genScanCode")
public Message<HashMap<String,String>> genScanCode() {
log.debug("/genScanCode.");
UserInfo userInfo = AuthorizationUtils.getUserInfo();
Long ticket = scanCodeService.createTicket();
String ticketString = userInfo == null ? ticket.toString() : ticket+","+userInfo.getId();
log.debug("ticket string {}",ticketString);
String encodeTicket = PasswordReciprocal.getInstance().encode(ticketString);
BufferedImage bufferedImage = RQCodeUtils.write2BufferedImage(encodeTicket, "gif", 300, 300);
String rqCode = Base64Utils.encodeImage(bufferedImage);
HashMap<String,String> codeMap = new HashMap<>();
codeMap.put("rqCode", rqCode);
codeMap.put("ticket", encodeTicket);
return new Message<>(Message.SUCCESS, codeMap);
}
} }