本文实现的是一个简单的自定义登录页,所以只需要配置Mysql即可
因为需要使用到Mysql和MyBatis所以需要添加相关的依赖
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.1.2version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<scope>runtimescope>
dependency>
dependencies>
在application.properties中配置Mysql()
// An highlighted block
spring.datasource.url=jdbc:mysql://localhost:3306/blogrepository?useUnicode=true&characterEncoding=UTF8&serverTimezone=GMT
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
通过上面的配置即可配置好Mysql,需要注意的时,新版本的Mysql在连接时需要指定UTC时区,通过serverTimeZone=GMT来指定。否则会出现下面的异常报错:
Java.sql.SQLException: The server time zone value ‘…???’ is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.
id | username | passwd |
---|---|---|
1 | root | root |
@Component
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private int id;
private String username;
private String passwd;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPasswd() {
return passwd;
}
public void setPasswd(String passwd) {
this.passwd = passwd;
}
}
通过Mybatis的注解来实现数据库的访问
@Mapper
@Repository
public interface UserMapper {
@Select("select id,username,passwd from user")
List<User> select();
}
@Service
@Transactional
public class AuthencationService implements UserDetailsService {
@Autowired
private UserMapper userMapper;
//实现该接口来查询对应的用户
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("前台用户名:"+username);
List<User> userList= userMapper.select();
User user = null;
for (User userone : userList){
System.out.println("数据库用户:" + userone.getUsername());
if (userone.getUsername().equals(username)){
user = userone;
break;
}
}
if (user == null){
System.out.println("不存在该用户");
throw new UsernameNotFoundException("用户不存在");
}
//添加用户权限。此处应该实现一个与User关联的Role表,以指定用户权限
List<GrantedAuthority> authorities = new ArrayList<>();
//Sercurity的config的hasAnyRole方法会给权限添加ROLE_的前缀,所以必须要加上该前缀
authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
return new org.springframework.security.core.userdetails.User(user.getUsername()
,user.getPasswd(),authorities);
}
}
需要注意的是,在给用户添加权限时,需要加上ROLE_前缀,因为SpringSecurity会自动给设置的权限加上ROLE_前缀。此类为了简略所以直接添加了用户权限,实际应该实现一个与用户关联的权限表,从权限表中添加相关的权限。类中的控制台输出语句可以去掉。
@Controller
public class LoginController {
@GetMapping("/authentication/login")
public String authenticationLogin() throws IOException{
return "login";
}
@GetMapping("/admin/index")
public String indexPage(){
return "index";
}
}
@Component
public class AuthenticationSucessHandler extends SimpleUrlAuthenticationSuccessHandler {
//Spring Security 通过RedirectStrategy对象来负责所有重定向事务
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
//重写handle方法,来指定重定向的url
@Override
protected void handle(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
String targetUrl = determineTargetUrl(authentication);
redirectStrategy.sendRedirect(request,response,targetUrl);
}
private String determineTargetUrl(Authentication authentication){
String url="";
//获取当前登录用户的角色权限合集
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
//保存角色信息
List<String> roles = new ArrayList<>();
for (GrantedAuthority authority : authorities){
System.out.println("拥有的权限为:" + authority.getAuthority());
roles.add(authority.getAuthority());
}
//根据权限选择不同的Url,暂不做处理
url = "/admin/index";
return url;
}
//判断角色是否为ROLE_ADMIN
private boolean isAdmin(List<String> roles){
for (String str : roles){
if (str.equals("ROLE_ADMIN")){
return true;
}
}
return false;
}
}
@Component
public class CustmProvider implements AuthenticationProvider {
@Autowired
AuthencationService service;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
//获取前端返回的用户名和密码
UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken)
authentication;
String username = token.getName();//前台用户名
String passwd = token.getCredentials().toString();//前台的密码
System.out.println("前台密码:"+passwd);
User user = (User) service.loadUserByUsername(username);
if (user == null){
throw new UsernameNotFoundException("Could not find the user!");
}
if (!user.getPassword().equals(passwd)){
throw new BadCredentialsException("Password wrong!");
}
System.out.println("验证通过");
return new UsernamePasswordAuthenticationToken(user,user.getPassword(),user.getAuthorities());
}
@Override
public boolean supports(Class<?> aClass) {
return true;
}
}
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private CustmProvider custmProvider;
@Autowired
private AuthenticationSucessHandler authenticationSucessHandler;
//设置密码的加密方式
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/authentication/*","/login")
.permitAll()
.antMatchers("/user/**").hasAnyRole("USER","ADMIN")
.antMatchers("/admin/**").hasAnyRole("ADMIN")
.antMatchers("**.html").hasAnyRole("NONE")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/authentication/login")//设置登录的路径(和Controller中的Mapping路径一致)
.successHandler(authenticationSucessHandler)
.loginProcessingUrl("/authentication/form")//处理前端数据的路径
.usernameParameter("username")
.passwordParameter("password");
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(custmProvider);
}
}
<head>
<meta charset="UTF-8">
<title>Logintitle>
head>
<body>
<form name="loginForm" th:action="@{/authentication/form}" th:method="post">
<table>
<tr>
<td>UserName:td>
<td><input th:type="text" name="username" id="username" placeholder="UserName">td>
tr>
<tr>
<td>Password:td>
<td><input th:type="password" name="password" id="password" placeholder="Password">td>
tr>
<tr>
<td><input th:type="submit" value="Login">td>
<td><input th:type="reset" value="Reset">td>
tr>
table>
form>
body>
html>
这里需要注意的地方是表格的action属性需要与security配置类的.loginProcessingUrl()方法设置的属性一致才并且提交的方式须为POST,这样才能正确的处理登录的数据,CusmProvider会无法获取到前端的数据。